﻿/*
    Copyright (C) 2012  Milton da Silva Junior - admin.jr.dotnet@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Loja.Data;
using System.Web.Mvc;
using Loja.DataDTO;
using Loja.Business.Validacao.ItemPedido;
using Loja.Business.Validacao.Pedido;
using Loja.Business.Validacao;

namespace Loja.Business
{
    public class PedidoBLL
    {
        private IRepository<Pedido> repsository;
        RepositoryFactory<Pedido> factory;

        public PedidoBLL(bool LazyLoadingEnabled)
        {
            factory = new RepositoryFactory<Pedido>();
            repsository = factory.CriarRepositorioDados(LazyLoadingEnabled);
        }

        public PedidoBLL()
        {
        }

        ~PedidoBLL()
        {
            if (repsository != null)
                repsository.Dispose();
        }

        public int Inserir(PedidoDTO pedidoDTO)
        {
            var pedido = new Pedido
            {
                Codigo = pedidoDTO.CodigoPedido,
                IdCliente = pedidoDTO.IdPessoa,
                Status = pedidoDTO.Status,
                Data = pedidoDTO.Data,
            };
            repsository.Add(pedido);
            repsository.SaveChanges();

            return pedido.IdPedido;
        }

        private string GerarCodigo()
        {
            var QtdPedidos = repsository.GetAll().Count();

            if (QtdPedidos == 0)
                QtdPedidos = 1;
            else
                QtdPedidos++;

            return string.Format("{0:00000000}", QtdPedidos);
        }

        private IEnumerable<Pedido> ListarPedido(int page, int rp, ref int total)
        {
            var pedidos = repsository.GetAll().OrderByDescending(p => p.IdPedido);
            total = pedidos.Count();
            return pedidos.Skip((page - 1) * rp).Take(rp).ToList();
        }

        private IEnumerable<ItemPedido> ListarItemPedido(int idPedido)
        {
            var repositoryItemPedido = new DataRepository<ItemPedido>(true);
            return repositoryItemPedido.Find(ip => ip.IdPedido == idPedido);
        }

        public PedidoDTO GetPedidoDTO(int idPedido)
        {
            PedidoDTO pedidoDTO = new PedidoDTO();

            var repositoryIndicador = new DataRepository<Indicador>(true);
            var status = repositoryIndicador.Find(s => s.TipoIndicador.Nome.Equals("StatusPedido"));

            pedidoDTO.Status = status.Single(i => i.TipoIndicador.Nome.Equals("StatusPedido") && i.Sigla.Equals("INC")).IdIndicador;

            if (idPedido == 0)
            {
                pedidoDTO.CodigoPedido = GerarCodigo();
                pedidoDTO.Data = DateTime.Now;
                return pedidoDTO;
            }

            var pedido = repsository.Single(p => p.IdPedido == idPedido);
            var repositoryPessoa = new DataRepository<Pessoa>(false);

            pedidoDTO.IdPedido = pedido.IdPedido;

            if (pedido.IdCliente != null && pedido.IdCliente > 0)
            {
                pedidoDTO.IdPessoa = (int)pedido.IdCliente;
                pedidoDTO.NomeCliente = repositoryPessoa.First(p => p.IdPessoa == pedido.IdCliente).Nome;
            }
            pedidoDTO.Status = pedido.Status;
            pedidoDTO.DscStatus = status.First(s => s.IdIndicador == pedido.Status).Descricao;
            pedidoDTO.CodigoPedido = pedido.Codigo;

            repositoryIndicador.Dispose();
            repositoryPessoa.Dispose();

            return pedidoDTO;
        }

        public IEnumerable<PedidoDTO> GetListaPedidoDTO(int page, int rp, ref int total, int idUsuarioLogado)
        {
            List<PedidoDTO> listaPedidoDTO = new List<PedidoDTO>();
            var repositoryIndicador = new DataRepository<Indicador>(true);
            var repositoryPessoa = new DataRepository<Pessoa>(false);

            PedidoDTO pedidoDTO;

            var status = repositoryIndicador.Find(s => s.TipoIndicador.Nome.Equals("StatusPedido"));

            RemoverPedidosComStatusImcompleto(status.Single(s => s.Sigla.Equals("INC")).IdIndicador, idUsuarioLogado);

            var listaPedido = ListarPedido(page, rp, ref total);

            if (listaPedido.Count() > 0)
            {
                foreach (var pedido in listaPedido)
                {
                    pedidoDTO = new PedidoDTO();

                    pedidoDTO.IdPedido = pedido.IdPedido;
                    pedidoDTO.CodigoPedido = pedido.Codigo;
                    pedidoDTO.Data = pedido.Data;

                    if (pedido.IdCliente != null && pedido.IdCliente > 0)
                    {
                        pedidoDTO.IdPessoa = (int)pedido.IdCliente;
                        pedidoDTO.NomeCliente = repositoryPessoa.Single(p => p.IdPessoa == pedido.IdCliente).Nome;
                    }

                    pedidoDTO.Status = pedido.Status;
                    pedidoDTO.DscStatus = status.First(s => s.IdIndicador == pedido.Status).Descricao;

                    decimal valorTotal = 0;

                    foreach (var itemPedido in pedido.ItemPedido)
                    {
                        valorTotal = valorTotal + (itemPedido.Valor * itemPedido.Quantidade);
                    }
                    pedidoDTO.ValorTotal = string.Format("{0:C}", valorTotal);

                    listaPedidoDTO.Add(pedidoDTO);
                }
                
            }

            repositoryIndicador.Dispose();
            repositoryPessoa.Dispose();

            return listaPedidoDTO;
        }

        private void RemoverPedidosComStatusImcompleto(int statusImcompleto, int idUsuarioLogado)
        {
            var estoqueRepositorio = new DataRepository<Estoque>();
            var pedidoRepositorio = new DataRepository<Pedido>();

            repsository.Context.Connection.Open();
            pedidoRepositorio.Context.Connection.Open();
            estoqueRepositorio.Context.Connection.Open();

            var transacaoEstoque = estoqueRepositorio.Context.Connection.BeginTransaction();
            var transcaoPedido = repsository.Context.Connection.BeginTransaction();
            var tranctionPedido = pedidoRepositorio.Context.Connection.BeginTransaction();

            try
            {
                var pedidosImcompletos = repsository.Find(p => p.Status == statusImcompleto && p.Data.Date < DateTime.Now.Date);

                foreach (Pedido pedidoImcompleto in pedidosImcompletos)
                {
                    if (pedidoImcompleto.ItemPedido.Count() > 0)
                    {
                        foreach (ItemPedido item in pedidoImcompleto.ItemPedido)
                        {
                            var estoque = estoqueRepositorio.Single(e => e.Tamanho.Equals(item.Numeracao) && e.IdProduto == item.IdProduto);
                            RealizarReposicaoEstoque(estoque, item);
                            GravarReposicaoEstoque(estoque, item, idUsuarioLogado);
                        }
                        estoqueRepositorio.SaveChanges();
                    }
                    var pedido = pedidoRepositorio.Single(p => p.IdPedido == pedidoImcompleto.IdPedido);
                    pedidoRepositorio.Delete(pedido);
                }
                pedidoRepositorio.SaveChanges(System.Data.Objects.SaveOptions.DetectChangesBeforeSave);


                transacaoEstoque.Commit();
                tranctionPedido.Commit();
                transcaoPedido.Commit();
            }
            catch (Exception ex)
            {
                transacaoEstoque.Rollback();
                transcaoPedido.Rollback();
                tranctionPedido.Rollback();

                throw ex;
            }
            finally
            {
                if (repsository.Context.Connection.State == System.Data.ConnectionState.Open)
                    repsository.Context.Connection.Close();

                if (estoqueRepositorio.Context.Connection.State == System.Data.ConnectionState.Open)
                    estoqueRepositorio.Context.Connection.Close();
            }
        }

        public IList<ItemPedidoDTO> ListaItemPedidoDTO(int idPedido)
        {
            var itemPedidoDTO = new ItemPedidoDTO();
            IList<ItemPedidoDTO> listaItemPedidoDTO = new List<ItemPedidoDTO>();

            foreach (var itemPedido in ListarItemPedido(idPedido))
            {
                itemPedidoDTO.IdPedido = itemPedido.IdPedido;
                itemPedidoDTO.IdProduto = itemPedido.IdProduto;
                itemPedidoDTO.Codigo = itemPedido.Produto.Codigo;
                itemPedidoDTO.CategoriaProduto = itemPedido.Produto.Categoria.Categoria2.Descricao;
                itemPedidoDTO.DscSubcategoriaProduto = itemPedido.Produto.Categoria.Descricao;
                itemPedidoDTO.Valor = string.Format("{0:C}", itemPedido.Valor);
                itemPedidoDTO.ValorTotal = string.Format("{0:C}", itemPedido.Valor * itemPedido.Quantidade);
                itemPedidoDTO.Tamanho = itemPedido.Numeracao;
                itemPedidoDTO.Quantidade = itemPedido.Quantidade;

                listaItemPedidoDTO.Add(itemPedidoDTO);
            }
            return listaItemPedidoDTO;
        }

        public bool ProdutoExiste(int idProduto)
        {
            var repositoryProduto = new DataRepository<Produto>(false);
            return repositoryProduto.Find(p => p.IdProduto == idProduto).Count() == 1;
        }

        public bool PedidoExiste(int idPedido)
        {
            return repsository.Find(p => p.IdPedido == idPedido).Count() == 1;
        }

        public bool PedidoPossuiItem(int idPedido)
        {
            return repsository.Single(p => p.IdPedido == idPedido).ItemPedido.Count() > 0;
        }

        public bool ExisteEstoqueProduto(int idProduto, int quantidadeSolicitada, ref int quantidadeDisponivel, string tamanho)
        {
            var repositorioEstoque = new DataRepository<Estoque>();

            if (ProdutoExiste(idProduto) && repositorioEstoque.Find(e => e.IdProduto == idProduto && e.Tamanho.Equals(tamanho)).Count() > 0)
            {
                quantidadeDisponivel = repositorioEstoque.Single(e => e.IdProduto == idProduto && e.Tamanho.Equals(tamanho)).Quantidade;
                return quantidadeDisponivel >= quantidadeSolicitada;
            }
            return false;
        }

        private string ValidarItemPedido(ItemPedidoDTO itemPedidoDTO)
        {
            IList<IValida<ItemPedidoDTO>> listaValidacao = new List<IValida<ItemPedidoDTO>>();

            listaValidacao.Add(new ValidaCodigoProdutoNaoInformado());
            listaValidacao.Add(new ValidaProdutoNaoEncontrado());
            listaValidacao.Add(new ValidarQuantidadeNaoInformada());
            listaValidacao.Add(new ValidaEstoqueIndisponivel());
 
            var validaItemPedido = new ValidaItemPedido();
            validaItemPedido.Validar(itemPedidoDTO, listaValidacao);

            return validaItemPedido.Erros.ToString().Trim();
        }

        public string SalvarItemPedido(ItemPedidoDTO itemPedidoDTO)
        {
            string erros = ValidarItemPedido(itemPedidoDTO);

            if (!string.IsNullOrEmpty(erros))
                return erros;

            int idProduto = Convert.ToInt32(itemPedidoDTO.Codigo);

            var produtoRepository = new DataRepository<Produto>(true);
            var produto = produtoRepository.Single(p => p.IdProduto == idProduto);
            var estoque = produto.Estoque.Single(e => e.Tamanho.Equals(itemPedidoDTO.Tamanho));

            RealizarBaixaEstoque(estoque, itemPedidoDTO);
            GravarMovimentacaoEstoque(estoque, itemPedidoDTO);
            AdicionarItemPedido(produto, itemPedidoDTO);

            if (!PedidoExiste(itemPedidoDTO.IdPedido))
                return string.Format("O Pedido número: {0:00000000} foi removido do sistema por outro usuário.", itemPedidoDTO.IdPedido);

            produtoRepository.SaveChanges(System.Data.Objects.SaveOptions.DetectChangesBeforeSave);
            produtoRepository.Dispose();

            return string.Empty;
        }

        private void RealizarBaixaEstoque(Estoque estoque, ItemPedidoDTO itemPedidoDTO)
        {
            estoque.Quantidade -= itemPedidoDTO.Quantidade;
        }

        private void RealizarReposicaoEstoque(Estoque estoque, ItemPedido item)
        {
            estoque.Quantidade += item.Quantidade;
        }

        private void GravarReposicaoEstoque(Estoque estoque, ItemPedido item, int idUsuarioLogado)
        {
            var repositorioIndicador = new DataRepository<Indicador>(true);
            int tipoMovimentacao = repositorioIndicador.Single(tm => tm.Sigla.Equals("REP") && tm.TipoIndicador.Nome.Equals("TipoMovimentacao")).IdIndicador;

            estoque.MovimentacaoEstoque.Add(new MovimentacaoEstoque
            {
                Quantidade = item.Quantidade,
                TipoMovimentacao = tipoMovimentacao,
                Data = DateTime.Now,
                IdRespMovimentacao = idUsuarioLogado,
            });

            repositorioIndicador.Dispose();
        }

        private void GravarMovimentacaoEstoque(Estoque estoque, ItemPedidoDTO itemPedidoDTO)
        {
            var indicadorRepository = new DataRepository<Indicador>(true);

            int tipoMovimentacao = indicadorRepository.Single(i => i.Sigla.Equals("BAI") && i.TipoIndicador.Nome.Equals("TipoMovimentacao")).IdIndicador;

            estoque.MovimentacaoEstoque.Add(new MovimentacaoEstoque
            {
                Data = DateTime.Now,
                IdEstoque = estoque.IdEstoque,
                Quantidade = itemPedidoDTO.Quantidade,
                TipoMovimentacao = tipoMovimentacao,
                IdRespMovimentacao = itemPedidoDTO.IdUsuarioLogado,
            });

            indicadorRepository.Dispose();
        }

        private void AdicionarItemPedido(Produto produto, ItemPedidoDTO itemPedidoDTO)
        {
            produto.ItemPedido.Add(new ItemPedido
            {
                IdPedido = itemPedidoDTO.IdPedido,
                Numeracao = itemPedidoDTO.Tamanho,
                Quantidade = itemPedidoDTO.Quantidade,
                Valor = Convert.ToDecimal(itemPedidoDTO.Valor),
            });
        }

        private string ValidarPedido(PedidoDTO pedidoDTO)
        {
            IList<IValida<PedidoDTO>> listaValidacoes = new List<IValida<PedidoDTO>>();

            listaValidacoes.Add(new ValidaCompradorNaoInformado());
            listaValidacoes.Add(new ValidaPedidoPossuiItem());
            ValidaPedido validaPedido = new ValidaPedido();
            validaPedido.Validar(pedidoDTO, listaValidacoes);

            return validaPedido.Erros.ToString().Trim();
        }

        public string FecharPedido(PedidoDTO pedidoDTO)
        {
            string erros = ValidarPedido(pedidoDTO);

            if (!string.IsNullOrEmpty(erros))
                return erros;

            var indicadorRepository = new DataRepository<Indicador>(true);
            int statusConfirmado = indicadorRepository.Single(i => i.TipoIndicador.Nome.Equals("StatusPedido") && i.Sigla.Equals("CON")).IdIndicador;

            var pedido = repsository.Single(p => p.IdPedido == pedidoDTO.IdPedido);
            pedido.Status = statusConfirmado;
            repsository.SaveChanges();

            return string.Empty;
        }

        public Pedido GetPedidoPorId(int idPedido)
        {
            return repsository.Single(p => p.IdPedido == idPedido);
        }
    }
}
